/* COPYRIGHT_CHUNFENG */
#define _XOPEN_SOURCE
#include <unistd.h>
#include <time.h>
#include "cf_std.h"
#include "cf_common.h"
#include "cf_list_file.h"
#include "cf_string.h"
#include "cf_shadow_file.h"
#include "lcf_log.h"

static int __shadow_atoi(char* start, char** sep)
{
	if((*sep = strchr(start, ':'))){
		if(*sep == start){
			return -1;
		}else{
			return cf_atoll_n(start, *sep-start);
		}
	}else{
		return -2;
	}
}

CFListHead* parse_shadow_line_new(char* start, char* end)
{
	CFShadow* shadow = (CFShadow*)malloc(sizeof(CFShadow));
	char* sep;
	if((sep = strchr(start, ':'))){
		shadow->name = cf_string_new_start_end(start, sep-1);
	}else{
		goto error_out;
	}
	start = sep+1;
	if((sep = strchr(start, ':'))){
		shadow->e_password = cf_string_new_start_end(start, sep-1);
	}else{
		goto error_out;
	}

	if((shadow->p_last_change = __shadow_atoi(sep+1, &sep)) == -2)
		goto error_out;
	if((shadow->p_min_age = __shadow_atoi(sep+1, &sep)) == -2)
		goto error_out;
	if((shadow->p_max_age = __shadow_atoi(sep+1, &sep)) == -2)
		goto error_out;
	if((shadow->p_warn_period = __shadow_atoi(sep+1, &sep)) == -2)
		goto error_out;
	if((shadow->p_inactive_period = __shadow_atoi(sep+1, &sep)) == -2)
		goto error_out;
	if((shadow->a_expire_date = __shadow_atoi(sep+1, &sep)) == -2)
		goto error_out;

	shadow->reserved = -1;
    cf_list_init(&shadow->list);
	return &shadow->list;

error_out:
	free(shadow);
	return NULL;
}

CFShadow* cf_shadow_get_next(CFShadow* node)
{
    return container_of(cf_list_get_next(&node->list), CFShadow, list);
}

void cf_shadow_free(CFShadow* shadow)
{
	cf_string_free(shadow->name);
	cf_string_free(shadow->e_password);
	free(shadow);
}

void cf_shadow_free_by_list(CFListHead* list)
{
    cf_shadow_free(container_of(list, CFShadow, list));
}

int __shadow_write_string(CFString* str, int fd)
{
    write(fd, cf_string_get_str(str), cf_string_get_len(str));
    write(fd, ":", 1);
    return 0;
}

int __shadow_write_int(int n, int fd)
{
    const char *tmp;
    int res = 0;
    if(n >= 0){
        tmp = cf_lltoa(n);
        write(fd, tmp, strlen(tmp));
    }
    write(fd, ":", 1);
    return res;
}

int cf_shadow_write(CFShadow* shadow, int fd)
{
    __shadow_write_string(shadow->name, fd);
    __shadow_write_string(shadow->e_password, fd);
    __shadow_write_int(shadow->p_last_change, fd);
    __shadow_write_int(shadow->p_min_age, fd);
    __shadow_write_int(shadow->p_max_age, fd);
    __shadow_write_int(shadow->p_warn_period, fd);
    __shadow_write_int(shadow->p_inactive_period, fd);
    __shadow_write_int(shadow->a_expire_date, fd);
    return 0;
}

int cf_shadow_write_by_list(CFListHead* list, int fd)
{
    return cf_shadow_write(container_of(list, CFShadow, list), fd);
}

CFShadow* cf_shadow_list_get_by_name(CFShadow* head, const char* name)
{
	CFShadow* tmpNode = cf_shadow_get_next(head);
	do{
		if(!strcmp(cf_string_get_str(tmpNode->name), name))
			return tmpNode;
		tmpNode= cf_shadow_get_next(tmpNode);
	}while(tmpNode != head);
    return NULL;
}

CFShadowFile* cf_shadow_file_new(const char* fileUri)
{
    CFShadowFile* file;
    CFListHead* head;

    file = (CFShadowFile*)malloc(sizeof(CFShadowFile));
    if(!file){
        LCF_ERR("Can't alloc for CFShadowFile\n");
        return NULL;
    }
    head = cf_list_file_new(fileUri, parse_shadow_line_new);
    if(!head){
        LCF_ERR("Parse shadow file: %s failed\n", fileUri);
        free(file);
        return NULL;
    }
    file->fileUri = cf_string_new(fileUri);
    cf_list_init(&file->head.list);
    cf_list_insert_pre(head, &file->head.list);
    return file;
}

void cf_shadow_file_free(CFShadowFile* file)
{
    cf_list_file_free(&file->head.list, cf_shadow_free_by_list);
    cf_string_free(file->fileUri);
    free(file);
}

int cf_shadow_file_write(CFShadowFile* file, int fd)
{
    return cf_list_file_write(&file->head.list, fd, cf_shadow_write_by_list);
}

int cf_shadow_file_print(CFShadowFile* file)
{
    return cf_shadow_file_write(file, 1);
}

int cf_shadow_file_save(CFShadowFile* file, const char* fileUri)
{
    if(!fileUri)
        return cf_list_file_save(&file->head.list, cf_string_get_str(file->fileUri), cf_shadow_write_by_list);
    return cf_list_file_save(&file->head.list, fileUri, cf_shadow_write_by_list);
}

int cf_shadow_file_set_password_by_name(CFShadowFile* file, const char* name, const char* password)
{
   CFShadow * shadow = cf_shadow_list_get_by_name(&file->head, name);
   if(shadow){
       cf_string_safe_cp(shadow->e_password, crypt(password, "$1$lijuan$"));
       shadow->p_last_change = time(NULL)/(24*60*60);
       return 0;
   }
   LCF_ERR("can not find username\n");
   return -1;
}
